package com.heima.wemedia.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.apis.article.IArticleClient;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.constants.WmNewsMessageConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.admin.dtos.AuthDto;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.*;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.utils.thread.WmThreadLocal;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsServiceImpl  extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private WmNewsAutoScanService wmNewsAutoScanService;
    @Autowired
    private WmNewsTaskService wmNewsTaskService;
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;
    @Autowired
    private WmChannelService wmChannelService;
    @Autowired
    private WmUserService wmUserService;
    @Autowired
    private IArticleClient iArticleClient;


    @Override
    public ResponseResult getList(NewsAuthDto dto) {
        if (dto==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.setPage((dto.getPage()-1)*dto.getSize());
        List<WmNewsVo> pageList = baseMapper.pageList(dto);
        PageResponseResult res = new PageResponseResult(dto.getPage(), dto.getSize(), pageList.size());
        return res.ok(pageList);
    }

    @Override
    public ResponseResult authPass(NewsAuthDto dto) {
        if (dto==null || dto.getId()==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews news = getById(dto.getId());
        if (news==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (news.getStatus()!=3) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"审核状态无效");
        }
        //审核成功,保存APP端相关文章数据
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(news,articleDto);
        articleDto.setLayout(news.getType());
        Integer channelId = news.getChannelId();
        WmChannel wmChannel = wmChannelService.getById(channelId);
        if (wmChannel != null) {
            articleDto.setChannelName(wmChannel.getName());
        }
        articleDto.setAuthorId(news.getUserId().longValue());
        WmUser wmUser = wmUserService.getById(news.getUserId());
        if (wmUser != null) {
            articleDto.setAuthorName(wmUser.getName());
        }
        if (news.getArticleId() != null) {
            //审核成功，说明此处不是首次审核通过的文章
            articleDto.setId(news.getArticleId());
        }
        articleDto.setCreatedTime(new Date());
        //保存APP端相关数据
        ResponseResult responseResult = iArticleClient.saveArticle(articleDto);
        if (!responseResult.getCode().equals(200)) {
            throw new RuntimeException("文章审核, 保存app端相关数据失败");
        }
        //回填审核通过的id
        news.setArticleId(((Long) responseResult.getData()));
        news.setStatus(((short) 9));
        news.setReason("审核通过");
        updateById(news);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public ResponseResult authFail(NewsAuthDto dto) {
        if (dto==null || dto.getId()==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews news = getById(dto.getId());
        if (news==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (news.getStatus()!=3) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"审核状态无效");
        }
        news.setStatus(WmNews.Status.FAIL.getCode());
        news.setReason("内容违规,请修改");
        updateById(news);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public ResponseResult down_or_up(Map<String,Object> dto) {
        if (dto==null || dto.isEmpty()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        Object id = dto.get("id");
        if (id==null || !(id instanceof Integer)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        WmNews news = getById(((Integer) id));
        if (news == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST.getCode(),"文章不存在");
        }
        if (news.getStatus()!=9) {
            return ResponseResult.errorResult(501,"文章未发布，不能上下架");
        }
        Object enable = dto.get("enable");
        if (enable==null || !(enable instanceof Integer)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        Integer enable1 = (Integer) enable;
        if (enable1<0 || enable1>1){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"参数错误");
        }
        update(Wrappers.lambdaUpdate(WmNews.class).set(WmNews::getEnable,enable1).eq(WmNews::getId,id));
        if (news.getArticleId() != null) {
            dto.put("articleId",news.getArticleId());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(dto));
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public ResponseResult del_news(Integer id) {
        if (id==null||id<=0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews news = getById(id);
        if (news == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (news.getStatus()==9) {
            return ResponseResult.errorResult(501,"文章已发布，不能删除");
        }
        removeById(id);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public ResponseResult showDetails(Integer id) {
        if (id==null||id<=0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews news = getById(id);
        if (news == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        return ResponseResult.okResult(news);
    }

    /**
     *  发布文章
     *   这里用隐式用this调用的方法 将不会被事务代理 ,这些方法若出现异常将不会触发回滚
     * @param dto 文章数据
     * @return
     */
    @Override
    public ResponseResult submitNews(WmNewsDto dto) {
        //参数校验
        if (dto==null || dto.getContent()==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //发布时间为空则设置当前时间
        Date publishTime = dto.getPublishTime();
        if (publishTime == null) {
            dto.setPublishTime(new Date());
        }
        //发布文章
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto,wmNews);

        //获取封面
        List<String> images = dto.getImages();
        //不为空则设置
        if (!CollectionUtils.isEmpty(images)) {
            StringJoiner joiner = new StringJoiner(",");
            images.forEach(joiner::add);
            wmNews.setImages(joiner.toString());
        }
        //封面类型
        Short type = dto.getType();
        //判断文章的类型
        switch (type){
            case -1: //自动
                wmNews.setType(null);
                break;
        }
        //若文章存在,则在删除原有素材关联后进行更新，反之新增文章
        wmNews.setCreatedTime(new Date());
        wmNews.setEnable(((short) 1));
        Optional.ofNullable(WmThreadLocal.getUser())
                .ifPresent(u->{
                    wmNews.setUserId(u.getId());
                });
        //说明文章需修改
        if (wmNews.getId()!=null) {
            wmNewsMaterialMapper.delete(Wrappers.lambdaQuery(WmNewsMaterial.class)
                    .eq(WmNewsMaterial::getNewsId,wmNews.getId()));
            updateById(wmNews);
        }else {
            //说明文章首次发布
            save(wmNews);
        }
        //若是草稿,则前面已经进行了数据库更新,返回即可
        if (dto.getStatus().equals(WmNews.Status.NORMAL.getCode())) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        //非草稿,需要保存现有的图文关系
        //首先提取文章内容的图片
        String content = dto.getContent();
        List<String> materials = new ArrayList<>();
        List<Map> maps = JSON.parseArray(content, Map.class);
        for (Map map : maps) {
            if (Objects.equals("image",map.get("image"))) {
                String url = (String) map.get("value");
                materials.add(url);
            }
        }
        //保存图文引用类型, 首先从素材库中查询相应的id列表 , 校验数量后 进行批量保存
        saveRelativeInfoForContent(materials,wmNews.getId());
        //* 若封面类型为自动，则设置封面类型的数据 ,然后更新文章信息
        //     *   若内容图片大于=1 小于3 则单图 1
        //     *   若内容图片大于=3 则多图 3
        //     *   反之无图 0
        //     *  保存封面图片与素材关系
        // 最后批量新增封面与文章的关系
        saveRelativeInfoForCover(dto,wmNews,materials);
        try {
            //等待持久化成功
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //异步审核：此处有可能会导致上一步未成功持久化到数据库,可能获取不到id
//        wmNewsAutoScanService.autoScanWmNews(wmNews.getId());
        wmNewsTaskService.addNesToTask(wmNews.getId(),dto.getPublishTime());
        return ResponseResult.okResult("操作成功");
    }

    /**
     * 若封面类型为自动，则设置封面类型的数据
     *   若内容图片大于=1 小于3 则单图 1
     *   若内容图片大于=3 则多图 3
     *   反之无图 0
     *  保存封面图片与素材关系
     * @param dto
     * @param wmNews
     * @param materials
     */
    private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
        List<String> images = dto.getImages();
        if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
            int size = materials.size();
            if(size>=3){
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images=materials.stream().limit(3).collect(Collectors.toList());
            } else if (size>= 1) {
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                images=materials.stream().limit(1).collect(Collectors.toList());
            }else {
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            //修改文章
            if (!CollectionUtils.isEmpty(images)) {
                StringJoiner joiner = new StringJoiner(",");
                images.forEach(joiner::add);
                wmNews.setImages(joiner.toString());
            }
            updateById(wmNews);
        }
        if (!CollectionUtils.isEmpty(images)) {
            saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);
        }
    }

    private void saveRelativeInfoForContent(List<String> materials, Integer id) {
         saveRelativeInfo(materials,id,WemediaConstants.WM_CONTENT_REFERENCE);
    }

    /**
     * 保存图文的关联
     * @param materials
     * @param id
     * @param wmContentReference
     */
    private void saveRelativeInfo(List<String> materials, Integer id, Short wmContentReference) {
        if(CollectionUtils.isEmpty(materials)){
            //若没有图片则省略
            return;
        }
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(Wrappers.lambdaQuery(WmMaterial.class)
                .in(WmMaterial::getUrl, materials));
        if(CollectionUtils.isEmpty(wmMaterials) || materials.size()!=wmMaterials.size()){
            //若没有素材 或 素材于
            throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
        }
        List<Integer> collect = wmMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
        wmNewsMaterialMapper.saveRelations(collect,id,wmContentReference);
    }

    private List<String> extractUrlInfo(String content) {
        List<String> ms = new ArrayList<>();
        List<Map> maps = JSON.parseArray(content, Map.class);
        for (Map map : maps) {
            if (Objects.equals("image",map.get("image"))) {
                String url = (String) map.get("value");
                ms.add(url);
            }
        }
        return ms;
    }

    private void saveOrUpdateWmNews(WmNews wmNews) {
        wmNews.setCreatedTime(new Date());
        wmNews.setEnable(((short) 1));
        Optional.ofNullable(WmThreadLocal.getUser())
                .ifPresent(u->{
                    wmNews.setUserId(u.getId());
                });
        //说明文章需修改
        if (wmNews.getId()!=null) {
            wmNewsMaterialMapper.delete(Wrappers.lambdaQuery(WmNewsMaterial.class)
                    .eq(WmNewsMaterial::getNewsId,wmNews.getId()));
            updateById(wmNews);
        }else {
            save(wmNews);
        }

    }

    @Override
    public ResponseResult findAll(WmNewsPageReqDto dto) {
        //1.检查参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //获取当前登录人的信息
        WmUser user = WmThreadLocal.getUser();
        if(user == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
       dto.checkParam();
        Page<WmNews> res = baseMapper.selectPage(
                new Page<WmNews>(dto.getPage(), dto.getSize()),
                Wrappers.lambdaQuery(WmNews.class)
                        .eq(dto.getStatus() != null, WmNews::getStatus, dto.getStatus())
                        .between(dto.getBeginPubDate()!=null && dto.getEndPubDate()!=null, WmNews::getPublishTime, dto.getBeginPubDate(),dto.getEndPubDate())
                        .eq(dto.getChannelId() != null, WmNews::getChannelId, dto.getChannelId())
                        .like(StringUtils.isNotBlank(dto.getKeyword() ), WmNews::getTitle, dto.getKeyword())
                        .eq(WmNews::getUserId, user.getId())
                        .orderByDesc(WmNews::getCreatedTime)
        );
        return new PageResponseResult(dto.getPage(),dto.getSize(), (int) res.getTotal()).ok(res.getRecords());
    }
}
